package blasthouse.characters;

import org.newdawn.slick.SpriteSheet;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Vector2f;

import blasthouse.objects.CollisionObject;
import blasthouse.objects.UpdatingGameElement;
import blasthouse.states.GamePlayState;

/**
 * This class should be abstract and force all children of it (the specific mob
 * types) to implement their own update, render, and specific variables and
 * actions
 * 
 * @author Eric
 */
public abstract class Mob implements UpdatingGameElement {
	private String name = null;
	private int currHP, maxHP, currSpriteRow, currSpriteCol;
	private float spriteLocX, spriteLocY;
	private SpriteSheet spriteSheet;
	private boolean spriteHFlip = false, isAlive = true, isOnScreen = false,
			isActivated = false, isOnGround = true, markForDelete = false;
	private Rectangle bounds = new Rectangle(0, 0, 0, 0);
	private Vector2f moveDirection = new Vector2f(0, 0);

	public Mob() {
	}

	public Vector2f getCenter() {
		return new Vector2f(this.spriteLocX + this.bounds.getWidth() / 2,
				this.spriteLocY + this.bounds.getHeight() / 2);
	}

	/**
	 * @param spriteSheet
	 *            the spriteSheet to set
	 */
	public void setSpriteSheet(SpriteSheet spriteSheet) {
		this.spriteSheet = spriteSheet;
	}

	/**
	 * @return the spriteSheet
	 */
	public SpriteSheet getSpriteSheet() {
		return this.spriteSheet;
	}

	/**
	 * @param spriteHFlip
	 *            the spriteHFlip to set
	 */
	public void setSpriteHFlip(boolean spriteHFlip) {
		this.spriteHFlip = spriteHFlip;
	}

	/**
	 * @return the spriteHFlip
	 */
	public boolean isSpriteHFlip() {
		return this.spriteHFlip;
	}

	/**
	 * @param bounds
	 *            the bounds to set
	 */
	public void setBounds(Rectangle bounds) {
		this.bounds = bounds;
	}

	/**
	 * @return the bounds
	 */
	public Rectangle getBounds() {
		return this.bounds;
	}

	/**
	 * @param currHP
	 *            the currHP to set
	 */
	public void setCurrHP(int currHP) {
		this.currHP = currHP;
	}

	/**
	 * @return the currHP
	 */
	public int getCurrHP() {
		return this.currHP;
	}

	/**
	 * @param maxHP
	 *            the maxHP to set
	 */
	public void setMaxHP(int maxHP) {
		this.maxHP = maxHP;
	}

	/**
	 * @return the maxHP
	 */
	public int getMaxHP() {
		return this.maxHP;
	}

	/**
	 * @param spriteLocX
	 *            the spriteLocX to set
	 */
	public void setSpriteLocX(float spriteLocX) {
		this.spriteLocX = spriteLocX;
	}

	/**
	 * @return the spriteLocX
	 */
	public float getSpriteLocX() {
		return this.spriteLocX;
	}

	/** * @param spriteLocY the spriteLocY to set */
	public void setSpriteLocY(float spriteLocY) {
		this.spriteLocY = spriteLocY;
	}

	/** * @return the spriteLocY */
	public float getSpriteLocY() {
		return this.spriteLocY;
	}

	/** * @param currSpriteRow the currSpriteRow to set */
	public void setCurrSpriteRow(int currSpriteRow) {
		this.currSpriteRow = currSpriteRow;
	}

	/** * @return the currSpriteRow */
	public int getCurrSpriteRow() {
		return this.currSpriteRow;
	}

	/** * @param currSpriteCol the currSpriteCol to set */
	public void setCurrSpriteCol(int currSpriteCol) {
		this.currSpriteCol = currSpriteCol;
	}

	/** * @return the currSpriteCol */
	public int getCurrSpriteCol() {
		return this.currSpriteCol;
	}

	/** * @param name the name to set */
	public void setName(String name) {
		this.name = name;
	}

	/** * @return the name */
	public String getName() {
		return this.name;
	}

	/** * @param isAlive the isAlive to set */
	public void setAlive(boolean isAlive) {
		this.isAlive = isAlive;
	}

	/** * @return the isAlive */
	public boolean isAlive() {
		return this.isAlive;
	}

	/** * @param isActivated the isActivated to set */
	public void setActivated(boolean isActivated) {
		this.isActivated = isActivated;
	}

	/** * @return the isActivated */
	public boolean isActivated() {
		return this.isActivated;
	}

	/** * @param spritePosition the spritePosition to set */
	public void setSpritePosition(Vector2f spritePosition) {
		this.spriteLocX = spritePosition.x;
		this.spriteLocY = spritePosition.y;
	}

	/** * @return the spritePosition */
	public Vector2f getSpritePosition() {
		return new Vector2f(this.spriteLocX, this.spriteLocY);
	}

	/** * @param isOnScreen the isOnScreen to set */
	public void setOnScreen(boolean isOnScreen) {
		this.isOnScreen = isOnScreen;
	}

	/*** @return the isOnScreen */
	public boolean isOnScreen() {
		// For now the screen is always 800x600, we check for "OnScreen" if the
		// mob within a 1000x600 box
		// to avoid the activation being visible to the player
		if (GamePlayState.Actors.get(0) instanceof Player) {
			// TODO: make this work for multi-player, maybe iterate through list
			// of players and do a distance
			// formula on each and check onscreen for them in order of proximity
			// until you get a true
			Player temp = ((Player) GamePlayState.Actors.get(0));
			// TODO: reference the screen size somehow to allow flexible
			// resolution options
			Rectangle test = new Rectangle(temp.getPlayerSpriteX() - 500,
					temp.getPlayerSpriteY() - 300, 1000, 600);
			return test.contains(this.getSpriteLocX(), this.getSpriteLocY());
		}
		return false;
	}

	/**
	 * @param isOnGround
	 *            the isOnGround to set
	 */
	public void setOnGround(boolean isOnGround) {
		this.isOnGround = isOnGround;
	}

	/** @return the isOnGround */
	public boolean isOnGround() {
		// Collides with floor/map
		boolean onGround = false;
		for (UpdatingGameElement floor : GamePlayState.GameObjects) {
			if (floor instanceof CollisionObject) {
				// Check vertical collisioning (read: falling)
				if (!onGround
						&& (Math.abs(this.bounds.getMaxY() - ((CollisionObject) floor).getBounds().getMinY()) < 4.5f)
						&& (((this.bounds.getMinX() > ((CollisionObject) floor).getBounds().getMinX())
								&& (this.bounds.getMinX() < ((CollisionObject) floor).getBounds().getMaxX())) 
						|| ((this.bounds.getMaxX() < ((CollisionObject) floor).getBounds().getMaxX())
								&& (this.bounds.getMaxX() > ((CollisionObject) floor).getBounds().getMinX())))) {
					onGround = true;
					// currJumpCount = 0; //Unused with mob? TODO: let some mobs
					// double jump?

					// TODO: Set bottom of mob to top of floor (smooth landing)
					this.setSpriteLocY(((CollisionObject) floor).getBounds()
							.getMinY()
							- this.getSpriteSheet()
									.getSprite(this.getCurrSpriteCol(),
											this.getCurrSpriteRow())
									.getHeight());

					// If the player is on something, and that something is
					// spikes... kill him
					if (onGround
							&& ((CollisionObject) floor).getName().startsWith(
									"spikes")) {
						this.moveDirection.set(new Vector2f(0, 0));
						this.setAlive(false);
					}
				}
				// Check horizontal collisioning
				if (((CollisionObject) floor).getBounds().intersects(
						this.bounds)) {
					Vector2f temp = new Vector2f();
					temp.set(
							((CollisionObject) floor).getBounds().getCenterX(),
							((CollisionObject) floor).getBounds().getCenterY());
					temp.sub(new Vector2f(this.bounds.getCenterX(), this.bounds
							.getCenterY()));
					if (Math.abs(temp.x) > Math.abs(temp.y)) {
						this.moveDirection.x = 0;
						this.spriteLocX += (temp.x >= 0 ? -1 : 1);
					} else {
						this.moveDirection.y = 0;
						this.spriteLocY += (temp.y >= 0 ? -1 : 1);
					}
				}
			}
		}
		return onGround;
	}

	/**
	 * @param moveDirection
	 *            the moveDirection to set
	 */
	public void setMoveDirection(Vector2f moveDirection) {
		this.moveDirection = moveDirection;
	}

	/** @return the moveDirection */
	public Vector2f getMoveDirection() {
		return this.moveDirection;
	}

	/**
	 * @param markForDelete
	 *            the markForDelete to set
	 */
	public void setMarkForDelete(boolean markForDelete) {
		this.markForDelete = markForDelete;
	}

	/** @return the markForDelete */
	public boolean isMarkForDelete() {
		return this.markForDelete;
	}
}
